/*
//
//              INTEL CORPORATION PROPRIETARY INFORMATION
//  This software is supplied under the terms of a license  agreement or
//  nondisclosure agreement with Intel Corporation and may not be copied
//  or disclosed except in  accordance  with the terms of that agreement.
//    Copyright (c) 2007-2008 Intel Corporation. All Rights Reserved.
//
//
*/

#include "umc_defs.h"
#if defined(UMC_ENABLE_AVS_VIDEO_DECODER)

#include <trace_log.h>
#include "umc_avs_dec_decompressor.h"
#include "umc_avs_sequence_header.h"
#include "umc_avs_picture_header.h"
#include "umc_avs_dec_bit_stream.h"
#include "umc_avs_mb_info.h"
#include "umc_avs_dec_tables.h"

namespace UMC
{

void AVSDecompressor::DecompressIMacroBlocksRow(void)
{
    MbMax = m_decCtx.MbIndex + m_decCtx.MbWidth;

    // we can use MbIndex either from decoding and reconstructing contexts
    MbIndex = m_decCtx.MbIndex;

    PrepareNeighbours(&m_decCtx);
    PrepareDecoding();
    PrepareReconstruction();

    do
    {
        if (0 == MbIndex)
        {
            WRITE_TRACE_LOG(_dec_tst, "Frame num is  ", m_decCtx.m_pPicHeader->picture_distance - 1);
            WRITE_TRACE_LOG(_rec_tst, "Frame num is  ", m_decCtx.m_pPicHeader->picture_distance - 1);
        }
        WRITE_TRACE_LOG_MB_TITLE(_dec_tst, MbIndex, m_decCtx.MbWidth, m_decCtx.MbHeight, 0 == m_decCtx.m_pPicHeader->progressive_frame);
        WRITE_TRACE_LOG_MB_TITLE(_rec_tst, MbIndex, m_recCtx.MbWidth, m_recCtx.MbHeight, 0 == m_recCtx.m_pPicHeader->progressive_frame);

        ResetMacroblock();

        // set coeffs buffers
        m_pWriteCoeffs = m_decCtx.m_pCoeffs;
        m_pReadCoeffs = m_recCtx.m_pCoeffs;

        // decode next macroblock
        DecodeIMacroBlockType();
        DecodeIMacroBlock();
        ReconstructIMacroBlock();

        MbIndex += 1;
        AdvanceNeighbours();
        AdvanceDecoding();
        AdvanceReconstruction();

    } while (MbIndex < MbMax);

    // update context
    FinalizeDecoding();
    FinalizeReconstruction();

} // void AVSDecompressor::DecompressIMacroBlocksRow(void)

void AVSDecompressor::DecodeIMacroBlocksRow(void)
{
    MbMax = m_decCtx.MbIndex + m_decCtx.MbWidth;

    // we can use MbIndex from decoding context only
    MbIndex = m_decCtx.MbIndex;

    // set coeffs buffers
    m_pWriteCoeffs = m_decCtx.m_pCoeffs;

    PrepareNeighbours(&m_decCtx);
    PrepareDecoding(true);

    do
    {
        WRITE_TRACE_LOG_MB_TITLE(_dec_tst, MbIndex, m_decCtx.MbWidth, m_decCtx.MbHeight, 0 == m_decCtx.m_pPicHeader->progressive_frame);

        ResetMacroblock();

        // decode next macroblock
        DecodeIMacroBlockType();
        DecodeIMacroBlock();

        MbIndex += 1;
        AdvanceNeighbours();
        AdvanceDecoding();

    } while (MbIndex < MbMax);

    // update context
    FinalizeDecoding();

} // void AVSDecompressor::DecodeIMacroBlocksRow(void)

void AVSDecompressor::DecodeIMacroBlockType(void)
{
    // set default parameters for I macroblock
    m_pMbInfo->MbType = I_8x8;
    m_pMbInfo->MbCBP = AVS_CBP_UNKNOWN;
    m_pMbInfo->QP = m_decCtx.PreviousQP;

} // void AVSDecompressor::DecodeIMacroBlockType(void)

void AVSDecompressor::DecodeIMacroBlock(void)
{
    Ipp32u i;

    // decode luminance prediction modes
    for (i = 0; i < 4; i += 1)
    {
        // get prediction mode for each block
        pred_mode_flag[i] = (Ipp8u) GetBit(&m_decCtx.m_stream);
        WRITE_TRACE_LOG(_dec_tst, "pred mode flag", pred_mode_flag[i]);
        if (0 == pred_mode_flag[i])
        {
            intra_luma_pred_mode[i] = (Ipp8s) GetBits(&m_decCtx.m_stream, 2);
            WRITE_TRACE_LOG(_dec_tst, "intra luma pred mode", intra_luma_pred_mode[i]);
        }
    }

    // restore prediction modes
    {
        AVS_MB_INFO *pLeft = m_pMbInfoLeft, *pTop = m_pMbInfoTop;

        // we can predict modes only from I macroblock(s)
        if ((pLeft) &&
            (I_8x8 != pLeft->MbType))
            pLeft = NULL;
        if ((pTop) &&
            (I_8x8 != pTop->MbType))
            pTop = NULL;

        SetIntraPredModeISlice(pLeft, pTop, 0);
        SetIntraPredModeISlice(m_pMbInfo, pTop, 1);
        SetIntraPredModeISlice(pLeft, m_pMbInfo, 2);
        SetIntraPredModeISlice(m_pMbInfo, m_pMbInfo, 3);
    }

    WRITE_TRACE_LOG(_dec_tst, "intraLumaPredMode", m_pMbInfo->IntraLumaPredMode[0]);
    WRITE_TRACE_LOG(_dec_tst, "intraLumaPredMode", m_pMbInfo->IntraLumaPredMode[1]);
    WRITE_TRACE_LOG(_dec_tst, "intraLumaPredMode", m_pMbInfo->IntraLumaPredMode[2]);
    WRITE_TRACE_LOG(_dec_tst, "intraLumaPredMode", m_pMbInfo->IntraLumaPredMode[3]);

    // decode chrominance prediction mode(s)
    m_pMbInfo->IntraChromaPredMode[0] = (IppIntraChromaPredMode_H264) GetUE(&m_decCtx.m_stream);
    WRITE_TRACE_LOG(_dec_tst, "intra chroma pred mode 0", m_pMbInfo->IntraChromaPredMode[0]);
    if (AVS_CHROMA_422_FORMAT == m_decCtx.m_pSeqHeader->chroma_format)
    {
        m_pMbInfo->IntraChromaPredMode[1] = (IppIntraChromaPredMode_H264) GetUE(&m_decCtx.m_stream);
        WRITE_TRACE_LOG(_dec_tst, "intra chroma pred mode 1", m_pMbInfo->IntraChromaPredMode[1]);
    }

    // decode coded block pattern
    if (AVS_CBP_UNKNOWN == m_pMbInfo->MbCBP)
    {
        Ipp32u uTemp;

        uTemp = GetUE(&m_decCtx.m_stream);
        WRITE_TRACE_LOG(_dec_tst, "CBP code", uTemp);
        // error handling
        if (63 < uTemp)
            throw (int) 0;
        m_pMbInfo->MbCBP = CBPOfMacroblock[AVS_INTRA][uTemp];
        if (AVS_CHROMA_422_FORMAT == m_decCtx.m_pSeqHeader->chroma_format)
        {
            uTemp = GetUE(&m_decCtx.m_stream);
            WRITE_TRACE_LOG(_dec_tst, "CBP code +", uTemp);
            m_pMbInfo->MbCBP |= uTemp << 6;
        }
    }

    // decode quantization parameter
    if ((m_pMbInfo->MbCBP) &&
        (0 == m_decCtx.FixedQP))
    {
        Ipp32s mb_qp_delta = 0;

        mb_qp_delta = GetSE(&m_decCtx.m_stream);
        WRITE_TRACE_LOG(_dec_tst, "mb qp delta", mb_qp_delta);
        if (63 < (Ipp32u) (m_pMbInfo->QP + mb_qp_delta))
            throw (int) 0;
        m_pMbInfo->QP += mb_qp_delta;
        m_decCtx.PreviousQP += mb_qp_delta;
    }

    // decode blocks coefficients
    {
        Ipp32u uBlockMask = 1;

        // decode luminance blocks
        for (i = 0; i < 4; i += 1, uBlockMask += uBlockMask)
        {
            if (m_pMbInfo->MbCBP & uBlockMask)
            {
                Ipp32s iNumCoeffs;

                ippiDecodeLumaBlockIntra_AVS_1u16s((Ipp32u **) &m_decCtx.m_stream.src,
                                                   &m_decCtx.m_stream.offset,
                                                   &iNumCoeffs,
                                                   m_pWriteCoeffs,
                                                   m_decCtx.ScanType);
                m_pMbInfo->NumCoeffs[i] = (Ipp8u) iNumCoeffs;
                m_pWriteCoeffs += 64;
            }
        }

        // decode chrominance blocks
        for (; i < m_decCtx.iNumberOfBlocks; i += 1, uBlockMask += uBlockMask)
        {
            if (m_pMbInfo->MbCBP & uBlockMask)
            {
                Ipp32s iNumCoeffs;

                ippiDecodeChromaBlock_AVS_1u16s((Ipp32u **) &m_decCtx.m_stream.src,
                                                &m_decCtx.m_stream.offset,
                                                &iNumCoeffs,
                                                m_pWriteCoeffs,
                                                m_decCtx.ScanType);
                m_pMbInfo->NumCoeffs[i] = (Ipp8u) iNumCoeffs;
                m_pWriteCoeffs += 64;
            }
        }
    }

} // void AVSDecompressor::DecodeIMacroBlock(void)

} // namespace UMC

#endif // defined(UMC_ENABLE_AVS_VIDEO_DECODER)
